
(function (window) {

KC.extend({
	/**
		Only support the following selector syntax.
		Patterns:
		*: any element
		E: an element of type E
		E[foo]: an E element with a "foo" attribute
		E[foo="bar"]: an E element whose "foo" attribute value is exactly equal to "bar"
		E.warning: an E element whose class is "warning" (the document language specifies how class is determined)
		E#myid: an E element with ID equal to "myid"
		E F: an F element descendant of an E element
		E > F: an F element child of an E element
		Reference:
		Selectors Level 3: http://www.w3.org/TR/css3-selectors/
	*/
	query : function(expr, root) {
		var arr = KC.queryAll(expr, root);
		return arr.length > 0 ? arr[0] : null;
	},
	queryAll : function(expr, root) {
		root = root || document;
		function escape(str) {
			if (typeof str != 'string') return str;
			return str.replace(/([^\w\-])/g, '\\$1');
		}
		function stripslashes(str) {
			return str.replace(/\\/g, '');
		}
		function cmpTag(tagA, tagB) {
			return tagA === '*' || tagA.toLowerCase() === escape(tagB.toLowerCase());
		}
		function byId(id, tag, root) {
			var arr = [];
			var doc = root.ownerDocument || root;
			var el = doc.getElementById(stripslashes(id));
			if (el) {
				if (cmpTag(tag, el.nodeName) && KC.isAncestor(root, el)) arr.push(el);
			}
			return arr;
		}
		function byClass(className, tag, root) {
			var doc = root.ownerDocument || root;
			var arr = [];
			if (root.getElementsByClassName) {
				var els = root.getElementsByClassName(stripslashes(className));
				for (var i = 0, len = els.length, el; i < len; i++) {
					el = els[i];
					if (cmpTag(tag, el.nodeName)) arr.push(el);
				}
			} else if (doc.querySelectorAll) {
				var els = doc.querySelectorAll((root.nodeName !== '#document' ? root.nodeName + ' ' : '') + tag + '.' + className);
				for (var i = 0, len = els.length, el; i < len; i++) {
					el = els[i];
					if (KC.isAncestor(root, el)) arr.push(el);
				}
			} else {
				var els = root.getElementsByTagName(tag);
				className = ' ' + className + ' ';
				for (var i = 0, len = els.length, el; i < len; i++) {
					el = els[i];
					if (el.nodeType == 1) {
						var cls = el.className;
						if (cls && (' ' + cls + ' ').indexOf(className) > -1) arr.push(el);
					}
				}
			}
			return arr;
		}
		function byName(name, tag, root) {
			var arr = [];
			var els = root.getElementsByName(stripslashes(name));
			for (var i = 0, len = els.length, el; i < len; i++) {
				el = els[i];
				if (cmpTag(tag, el.nodeName)) {
					if (el.getAttributeNode('name')) arr.push(el);
				}
			}
			return arr;
		}
		function byAttr(key, val, tag, root) {
			var arr = [];
			var els = root.getElementsByTagName(tag);
			for (var i = 0, len = els.length, el; i < len; i++) {
				el = els[i];
				if (el.nodeType == 1) {
					if (val === null) {
						if (KC.attr(el, key) !== null) arr.push(el);
					} else {
						if (val === escape(KC.attr(el, key))) arr.push(el);
					}
				}
			}
			return arr;
		}
		function select(expr, root) {
			var arr = [];
			var tag = expr.match(/^((?:\\.|[^.#\s\[<>])+)/) ? RegExp.$1.toLowerCase() : '*';
			if (expr.match(/#((?:[\w\-]|\\.)+)$/)) {
				arr = byId(RegExp.$1, tag, root);
			} else if (expr.match(/\.((?:[\w\-]|\\.)+)$/)) {
				arr = byClass(RegExp.$1, tag, root);
			} else if (expr.match(/\[((?:[\w\-]|\\.)+)\]/)) {
				arr = byAttr(RegExp.$1.toLowerCase(), null, tag, root);
			} else if (expr.match(/\[((?:[\w\-]|\\.)+)\s*=\s*['"]?((?:\\.|[^'"]+)+)['"]?\]/)) {
				var key = RegExp.$1.toLowerCase(), val = RegExp.$2;
				if (key === 'id') arr = byId(val, tag, root);
				else if (key === 'class') arr = byClass(val, tag, root);
				else if (key === 'name') arr = byName(val, tag, root);
				else arr = byAttr(key, val, tag, root);
			} else {
				var els = root.getElementsByTagName(tag);
				for (var i = 0, len = els.length, el; i < len; i++) {
					el = els[i];
					if (el.nodeType == 1) arr.push(el); 
				}
			}
			return arr;
		}
		var parts = [];
		var arr, re = /((?:\\.|[^\s>])+|[\s>])/g;
		while (arr = re.exec(expr)) {
			if (arr[1] !== ' ') parts.push(arr[1]);
		}
		var results = [];
		if (parts.length == 1) {
			return select(parts[0], root);
		}
		var el, isChild = false;
		for (var i = 0, len = parts.length; i < len; i++) {
			var part = parts[i];
			if (part === '>') {
				isChild = true;
				continue;
			}
			if (i > 0) {
				var els = [];
				KC.each(results, function(key, val) {
					KC.each(select(part, val), function(k, v) {
						if (isChild) {
							if (val === v.parentNode) els.push(v);
						} else {
							els.push(v);
						}
					});
				});
				results = els;
			} else {
				results = select(part, root);
			}
			if (results.length == 0) return []; 
		}
		return results;
	}
});

})(window);

